#include "../edit/EditStack.h"
#include "unittest.h"
#include <string.h>

TEST_GROUP (TestEditOperation)
{
}

TEST_CASE (TestEditOperation, testCopyString) 
{
    CEditOperation op;
    op.CopyString("hello");
    TEST_ASSERT (op.m_text);
    TEST_ASSERT (strcmp (op.m_text, "hello") == 0);
    op.CopyString("Hello World");
    TEST_ASSERT (op.m_text);
    TEST_ASSERT (strcmp (op.m_text, "Hello World") == 0);
}

class CTestEditStack : public CEditStack
{
public:
    CEditOperation* testGetCurOperationsForward(unsigned int* last_idx)
    {
        return GetCurOperationsForward(last_idx);
    }
    CEditOperation* testGetCurOperationsBackward(unsigned int* last_idx)
    {
        return GetCurOperationsBackward(last_idx);
    }
    int testMoveCurPosForward()
    {
        return MoveCurPosForward();
    }
    int testMoveCurPosBackward()
    {
        return MoveCurPosBackward();
    }
    CEditOperation** GetCircArray() {return m_circqueue;}
    unsigned int GetStart() {return m_uStart;}
    unsigned int GetEnd() {return m_uEnd;}
    unsigned int GetStackSize() {return m_uCount;}
    unsigned int GetCurPos() {return m_uCurPos;}
    bool IsEmpty() {return m_bEmpty;}

    void SetStart(unsigned int s) { m_uStart = s;}
    void SetEnd(unsigned int e) {m_uEnd = e;}
    void SetCurPos(unsigned int cp) {m_uCurPos = cp;}
};

TEST_GROUP (TestEditStack)
{
}

TEST_CASE (TestEditStack, AddOperations)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetCircArray());
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 1);
    TEST_ASSERT(stack.GetCurPos() == 0);
    TEST_ASSERT(op->m_uID == 0);

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 2);
    TEST_ASSERT(stack.GetCurPos() == 1);
    TEST_ASSERT(op->m_uID == 1);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 3);
    TEST_ASSERT(stack.GetCurPos() == 2);
    TEST_ASSERT(op->m_uID == 2);

    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 0);
    TEST_ASSERT(stack.GetCurPos() == 3);
    TEST_ASSERT(op->m_uID == 3);

    op = new CEditOperation;
    op->CopyString("5");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 1);
    TEST_ASSERT(stack.GetEnd() == 1);
    TEST_ASSERT(stack.GetCurPos() == 0);
    TEST_ASSERT(op->m_uID == 4);
}

TEST_CASE (TestEditStack, AddOperationsWithTransaction)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    stack.StartTransaction();
    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetCircArray());
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 1);
    TEST_ASSERT(stack.GetCurPos() == 0);
    TEST_ASSERT(op->m_uID == 0);

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 2);
    TEST_ASSERT(stack.GetCurPos() == 1);
    TEST_ASSERT(op->m_uID == 0);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 3);
    TEST_ASSERT(stack.GetCurPos() == 2);
    TEST_ASSERT(op->m_uID == 0);
    stack.EndTransaction();


    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 0);
    TEST_ASSERT(stack.GetEnd() == 0);
    TEST_ASSERT(stack.GetCurPos() == 3);
    TEST_ASSERT(op->m_uID == 1);

    op = new CEditOperation;
    op->CopyString("5");
    stack.AddOperation(op);
    TEST_ASSERT(stack.GetStackSize() == 4);
    TEST_ASSERT(stack.GetStart() == 3);
    TEST_ASSERT(stack.GetEnd() == 1);
    TEST_ASSERT(stack.GetCurPos() == 0);
    TEST_ASSERT(op->m_uID == 2);
}

TEST_CASE (TestEditStack, MoveCurPos)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("5");
    stack.AddOperation(op);

    int ret;
    ret = stack.testMoveCurPosForward();
    TEST_ASSERT(ret < 0);
    TEST_ASSERT (stack.GetCurPos() == 0);
    
    ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 3);

    ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 2);

    ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 1);

    ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == -1);
    TEST_ASSERT (stack.GetCurPos() == 1);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 2);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 3);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 0);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == -1);
    TEST_ASSERT (stack.GetCurPos() == 0);
}

TEST_CASE (TestEditStack, MoveCurPosWithTransaction)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);

    stack.StartTransaction();

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);
    TEST_ASSERT (stack.GetCurPos() == 1);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);

    stack.EndTransaction();
    TEST_ASSERT (stack.GetCurPos() == 2);

    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);
    TEST_ASSERT (stack.GetCurPos() == 3);
   
    int ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 2);

    ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 0);

    ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == -1);
    TEST_ASSERT (stack.GetCurPos() == 0);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 1);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 3);

    ret = stack.testMoveCurPosForward();
    TEST_ASSERT (ret == -1);
    TEST_ASSERT (stack.GetCurPos() == 3);
}

TEST_CASE (TestEditStack, AddOpWhenCurPosPointsToTransaction)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);

    stack.StartTransaction();

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);
    TEST_ASSERT (stack.GetCurPos() == 1);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);

    stack.EndTransaction();
    TEST_ASSERT (stack.GetCurPos() == 2);

    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);
    TEST_ASSERT (stack.GetCurPos() == 3);
   
    int ret = stack.testMoveCurPosBackward();
    TEST_ASSERT (ret == 0);
    TEST_ASSERT (stack.GetCurPos() == 2);

    op = new CEditOperation;
    op->CopyString("5");
    stack.AddOperation(op);
    TEST_ASSERT (stack.GetCurPos() == 3);
    TEST_ASSERT (stack.GetEnd() == 0);
    TEST_ASSERT (stack.GetStart() == 0);

    CEditOperation** arr = stack.GetCircArray();
    TEST_ASSERT (strcmp("1", arr[0]->m_text) == 0);
    TEST_ASSERT (strcmp("2", arr[1]->m_text) == 0);
    TEST_ASSERT (strcmp("3", arr[2]->m_text) == 0);
    TEST_ASSERT (strcmp("5", arr[3]->m_text) == 0);
}

TEST_CASE(TestEditStack, AddOpWhenCurPosPointsToATransactionAcrossUpperLimit)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);

    stack.StartTransaction();
    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);
    op = new CEditOperation;
    op->CopyString("5");
    stack.AddOperation(op);
    stack.EndTransaction();

    op = new CEditOperation;
    op->CopyString("6");
    stack.AddOperation(op);

    TEST_ASSERT (stack.GetEnd() == 2);
    TEST_ASSERT (stack.GetStart() == 2);
    TEST_ASSERT (stack.GetCurPos() == 1);

    stack.testMoveCurPosBackward();
    TEST_ASSERT (stack.GetEnd() == 2);
    TEST_ASSERT (stack.GetStart() == 2);
    TEST_ASSERT (stack.GetCurPos() == 0);

    op = new CEditOperation;
    op->CopyString("7");
    stack.AddOperation(op);
    TEST_ASSERT (stack.GetEnd() == 2);
    TEST_ASSERT (stack.GetStart() == 2);
    TEST_ASSERT (stack.GetCurPos() == 1);

    CEditOperation** arr = stack.GetCircArray();
    TEST_ASSERT (strcmp("5", arr[0]->m_text) == 0);
    TEST_ASSERT (strcmp("7", arr[1]->m_text) == 0);
    TEST_ASSERT (strcmp("3", arr[2]->m_text) == 0);
    TEST_ASSERT (strcmp("4", arr[3]->m_text) == 0);
}

TEST_CASE(TestEditStack, testGetCurOperations)
{
    CEditOperation* op;
    CTestEditStack stack;
    stack.SetStackSize(4);

    op = new CEditOperation;
    op->CopyString("1");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("2");
    stack.AddOperation(op);

    op = new CEditOperation;
    op->CopyString("3");
    stack.AddOperation(op);

    stack.StartTransaction();
    op = new CEditOperation;
    op->CopyString("4");
    stack.AddOperation(op);
    op = new CEditOperation;
    op->CopyString("5");
    stack.AddOperation(op);
    stack.EndTransaction();

    op = new CEditOperation;
    op->CopyString("6");
    stack.AddOperation(op);

    unsigned int idx = (unsigned int) (-1);
    op = stack.testGetCurOperationsBackward(&idx);
    TEST_ASSERT (strcmp (op->m_text, "6") == 0);
    TEST_ASSERT (idx == 1);
    op = stack.testGetCurOperationsBackward(&idx);
    TEST_ASSERT (!op);

    stack.testMoveCurPosBackward();
    idx = (unsigned int) (-1);
    op = stack.testGetCurOperationsBackward(&idx);
    TEST_ASSERT (idx == 0);
    TEST_ASSERT (strcmp (op->m_text, "5") == 0);
    op = stack.testGetCurOperationsBackward(&idx);
    TEST_ASSERT (idx == 3);
    TEST_ASSERT (strcmp (op->m_text, "4") == 0);

    stack.testMoveCurPosBackward();
    idx = (unsigned int) (-1);
    op = stack.testGetCurOperationsBackward(&idx);
    TEST_ASSERT (idx == 2);
    TEST_ASSERT (strcmp (op->m_text, "3") == 0);

    stack.testMoveCurPosForward();
    idx = (unsigned int) (-1);
    op = stack.testGetCurOperationsForward(&idx);
    TEST_ASSERT (idx == 3);
    TEST_ASSERT (strcmp (op->m_text, "4") == 0);

    op = stack.testGetCurOperationsForward(&idx);
    TEST_ASSERT (idx == 0);
    TEST_ASSERT (strcmp (op->m_text, "5") == 0);
    op = stack.testGetCurOperationsForward(&idx);
    TEST_ASSERT (!op);

    stack.testMoveCurPosForward();
    idx = (unsigned int) (-1);
    op = stack.testGetCurOperationsForward(&idx);
    TEST_ASSERT (idx == 1);
    TEST_ASSERT (strcmp (op->m_text, "6") == 0);
}